<template>
    <div style="position: relative; width: 100%; height: 100%">
        <canvas
            ref="canvasRef"
            :width="canvasWidth * rem"
            :height="canvasHeight * rem"
            style="
                position: absolute;
                left: 45%;
                top: 50%;
                transform: translate(-50%, -50%);
                display: block;
                cursor: pointer;
            "
            @mousemove="handleMouseMove"
            @mouseleave="handleMouseLeave"
            @click="handleClick"
            @mousedown="handleMouseDown"
        ></canvas>
        <button
            v-if="enableDrag"
            @click="exportCards"
            style="
                position: absolute;
                right: 50%;
                bottom: 10%;
                z-index: 999;
                padding: 8px 18px;
                font-size: 16px;
                background: #fff;
                border-radius: 6px;
                border: 1px solid #bbb;
                box-shadow: 0 2px 8px #0002;
                cursor: pointer;
            "
        >
            导出卡片数据
        </button>
        <div
            v-if="showExport"
            style="
                position: absolute;
                right: 60%;
                bottom: 10%;
                z-index: 1000;
                background: #222;
                color: #fff;
                padding: 16px;
                border-radius: 8px;
                box-shadow: 0 4px 24px #0008;
                width: 520px;
                max-width: 90vw;
            "
        >
            <div style="font-size: 15px; margin-bottom: 8px">
                请复制下方内容替换 cards：
            </div>
            <textarea
                ref="exportTextarea"
                :value="exportText"
                readonly
                style="
                    width: 100%;
                    height: 320px;
                    font-size: 14px;
                    font-family: monospace;
                    background: #111;
                    color: #fff;
                    border: 1px solid #444;
                    border-radius: 4px;
                    padding: 8px;
                    resize: none;
                "
            ></textarea>
            <div style="text-align: right; margin-top: 8px">
                <button
                    @click="showExport = false"
                    style="
                        padding: 4px 16px;
                        font-size: 15px;
                        border-radius: 4px;
                        border: none;
                        background: #666;
                        color: #fff;
                        cursor: pointer;
                    "
                >
                    关闭
                </button>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, onMounted, watch, nextTick } from "vue";
const bubbleImg = new window.Image();
bubbleImg.src =
    "https://s3plus.meituan.net/opapisdk/op_ticket_1_885190757_1761326185500_qdqqd_hxpbjk.png";
const props = defineProps({
    cards: Array,
    zoomedArr: Array,
});
const emit = defineEmits(["zoom", "cardClick"]);
const rem = ref(16);
const canvasWidth = ref(87.5);
const canvasHeight = ref(56.25);
const canvasRef = ref(null);
const images = ref([]); // 存放Image对象
const hoverIdx = ref(-1);
const draggingIdx = ref(-1);
const dragOffset = ref({ x: 0, y: 0 });
const selectedIdxs = ref([]); // 多选索引
const enableDrag = false; // 拖拽开关，开发时true，上线时false

const showExport = ref(false);
const exportText = ref("");
const exportTextarea = ref(null);

function exportCards() {
    // 只导出cards的所有字段，保持顺序
    const arr = props.cards.map((card) => ({
        img: card.img,
        video: card.video,
        name: card.name,
        left: Number(card.left.toFixed(2)),
        top: Number(card.top.toFixed(2)),
        width: Number(card.width),
        height: Number(card.height),
        nameTop: card.nameTop,
        nameRight: card.nameRight,
        nameBgImgWidht: card.nameBgImgWidht,
        nameBgImgHeight: card.nameBgImgHeight,
    }));
    exportText.value = JSON.stringify(arr, null, 2);
    showExport.value = true;
    nextTick(() => {
        if (exportTextarea.value) {
            exportTextarea.value.focus();
            exportTextarea.value.select();
        }
    });
}

function loadImages() {
    images.value = [];
    props.cards.forEach((card, idx) => {
        const img = new window.Image();
        img.crossOrigin = "anonymous";
        img.src = card.img;
        images.value[idx] = img;
        img.onload = () => redraw();
    });
    redraw();
}

watch(() => props.cards, loadImages, { deep: true });
watch(() => props.zoomedArr, redraw, { deep: true });

function redraw() {
    const ctx = canvasRef.value.getContext("2d");
    ctx.clearRect(
        0,
        0,
        canvasWidth.value * rem.value,
        canvasHeight.value * rem.value
    );
    for (let idx = props.cards.length - 1; idx >= 0; idx--) {
        const card = props.cards[idx];
        const img = images.value[idx];
        if (!img) continue;
        let scale = props.zoomedArr[idx] ? 1.2 : 1;
        if (draggingIdx.value !== -1 && selectedIdxs.value.includes(idx)) {
            scale = 1;
        }
        const w = card.width * scale * rem.value;
        const h = card.height * scale * rem.value;
        const x = (card.left + card.width / 2) * rem.value - w / 2;
        const y = (card.top + card.height) * rem.value - h;
        ctx.save();
        ctx.drawImage(img, x, y, w, h);
        // 多选高亮边框
        if (enableDrag && selectedIdxs.value.includes(idx)) {
            ctx.save();
            ctx.strokeStyle = "#00eaff";
            ctx.lineWidth = 4;
            ctx.strokeRect(x, y, w, h);
            ctx.restore();
        }
        // 显示 left/top 数值
        if (enableDrag) {
            ctx.font = "20px monospace";
            ctx.fillStyle = "#FFF";
            ctx.fillText(
                `left:${card.left.toFixed(2)} top:${card.top.toFixed(2)}`,
                x,
                y - 8
            );
        }
        // 名字字体大小
        const nameFontSize = rem.value * 1.2;
        ctx.font = `${nameFontSize}px FangSong, FangSong_GB2312, STFangsong, 仿宋, serif`;
        ctx.fillStyle = props.zoomedArr[idx] ? "#FFD700" : "#fff";
        ctx.textBaseline = "top";
        // 气泡宽高自动适配
        const bubbleW = Math.max(
            nameFontSize * 1.5,
            (card.nameBgImgWidht || 2) * rem.value
        );
        const bubbleH = Math.max(
            nameFontSize * card.name.length,
            (card.nameBgImgHeight || 8) * rem.value
        );
        // 气泡装饰
        if (bubbleImg.complete && bubbleImg.naturalWidth > 0) {
            ctx.drawImage(
                bubbleImg,
                x + w + (card.nameRight || 0) * rem.value - 10,
                y + (card.nameTop || 0) * rem.value,
                bubbleW,
                bubbleH
            );
        }
        const textHeight = card.name.length * nameFontSize;
        const textX =
            x + w + (card.nameRight || 0) * rem.value - 10 + bubbleW / 2;
        const textY =
            y + (card.nameTop || 0) * rem.value + (bubbleH - textHeight) / 2;
        ctx.textAlign = "center";
        for (let i = 0; i < card.name.length; i++) {
            ctx.fillText(card.name[i], textX, textY + i * nameFontSize);
        }
        ctx.save();
        ctx.restore();
        ctx.restore();
    }
}

function getCardAt(mx, my) {
    for (let idx = 0; idx < props.cards.length; idx++) {
        const card = props.cards[idx];
        const img = images.value[idx];
        if (!img || !img.complete || img.naturalWidth === 0) continue;
        const scale = props.zoomedArr[idx] ? 1.2 : 1;
        const w = card.width * scale * rem.value;
        const h = card.height * scale * rem.value;
        const x = (card.left + card.width / 2) * rem.value - w / 2;
        const y = (card.top + card.height) * rem.value - h;
        if (mx >= x && mx <= x + w && my >= y && my <= y + h) {
            const wInt = Math.round(w);
            const hInt = Math.round(h);
            const offCanvas = document.createElement("canvas");
            offCanvas.width = wInt;
            offCanvas.height = hInt;
            const offCtx = offCanvas.getContext("2d");
            offCtx.drawImage(img, 0, 0, wInt, hInt);
            const px = offCtx.getImageData(
                Math.round(mx - x),
                Math.round(my - y),
                1,
                1
            ).data;
            if (px[3] > 0) {
                return idx;
            }
        }
    }
    return -1;
}

function handleMouseMove(e) {
    const rect = canvasRef.value.getBoundingClientRect();
    const mx =
        (e.clientX - rect.left) *
        ((canvasWidth.value * rem.value) / rect.width);
    const my =
        (e.clientY - rect.top) *
        ((canvasHeight.value * rem.value) / rect.height);
    const idx = getCardAt(mx, my);
    if (idx !== hoverIdx.value) {
        hoverIdx.value = idx;
        props.cards.forEach((_, i) => emit("zoom", { val: i === idx, idx: i }));
    }
}

function handleMouseLeave() {
    hoverIdx.value = -1;
    props.cards.forEach((_, i) => emit("zoom", { val: false, idx: i }));
}

function handleClick(e) {
    const rect = canvasRef.value.getBoundingClientRect();
    const mx =
        (e.clientX - rect.left) *
        ((canvasWidth.value * rem.value) / rect.width);
    const my =
        (e.clientY - rect.top) *
        ((canvasHeight.value * rem.value) / rect.height);
    const idx = getCardAt(mx, my);
    if (!enableDrag) {
        if (idx !== -1) emit("cardClick", idx);
        return;
    }
    // 多选逻辑
    if (idx !== -1) {
        if (e.ctrlKey || e.shiftKey) {
            // 多选，已选则取消，未选则加入
            const i = selectedIdxs.value.indexOf(idx);
            if (i === -1) selectedIdxs.value.push(idx);
            else selectedIdxs.value.splice(i, 1);
        } else {
            // 单选
            selectedIdxs.value = [idx];
        }
        emit("cardClick", idx);
        redraw();
    }
}

function handleMouseDown(e) {
    if (!enableDrag) return;
    const rect = canvasRef.value.getBoundingClientRect();
    const mx =
        (e.clientX - rect.left) *
        ((canvasWidth.value * rem.value) / rect.width);
    const my =
        (e.clientY - rect.top) *
        ((canvasHeight.value * rem.value) / rect.height);
    const idx = getCardAt(mx, my);
    if (idx !== -1) {
        // 拖拽多选，如果当前idx未选则只拖当前，否则拖所有已选
        if (selectedIdxs.value.length > 1 && selectedIdxs.value.includes(idx)) {
            draggingIdx.value = -2; // -2表示多选拖拽
            dragOffset.value = { x: mx, y: my };
        } else {
            draggingIdx.value = idx;
            // 只有没按Ctrl/Shift时才重置为单选，否则保持多选
            if (!(e.ctrlKey || e.shiftKey)) {
                selectedIdxs.value = [idx];
            }
            const card = props.cards[idx];
            const scale = props.zoomedArr[idx] ? 1.2 : 1;
            const w = card.width * scale * rem.value;
            const h = card.height * scale * rem.value;
            const x = (card.left + card.width / 2) * rem.value - w / 2;
            const y = (card.top + card.height) * rem.value - h;
            dragOffset.value = {
                x: mx - x,
                y: my - y,
            };
        }
        window.addEventListener("mousemove", handleDragMove);
        window.addEventListener("mouseup", handleDragEnd);
    }
}

function handleDragMove(e) {
    if (draggingIdx.value === -1) return;
    const rect = canvasRef.value.getBoundingClientRect();
    const mx =
        (e.clientX - rect.left) *
        ((canvasWidth.value * rem.value) / rect.width);
    const my =
        (e.clientY - rect.top) *
        ((canvasHeight.value * rem.value) / rect.height);
    if (draggingIdx.value === -2) {
        // 多选拖拽，计算整体偏移
        const dx = mx - dragOffset.value.x;
        const dy = my - dragOffset.value.y;
        selectedIdxs.value.forEach((idx) => {
            const card = props.cards[idx];
            card.left += dx / rem.value;
            card.top += dy / rem.value;
        });
        dragOffset.value = { x: mx, y: my };
        redraw();
        return;
    }
    const idx = draggingIdx.value;
    const card = props.cards[idx];
    const scale = props.zoomedArr[idx] ? 1.2 : 1;
    const w = card.width * scale * rem.value;
    const h = card.height * scale * rem.value;
    // 计算新的 left/top（单位：rem.value）
    card.left = (mx - dragOffset.value.x + w / 2) / rem.value - card.width / 2;
    card.top = (my - dragOffset.value.y + h) / rem.value - card.height;
    redraw();
}

function handleDragEnd() {
    draggingIdx.value = -1;
    window.removeEventListener("mousemove", handleDragMove);
    window.removeEventListener("mouseup", handleDragEnd);
    redraw();
}

function setRemBase() {
    const baseSize = 12; // 基准字体大小
    const designWidth = 1920;
    const html = document.documentElement;
    // 保证最小宽度为1400
    const clientWidth = Math.max(html.clientWidth, 1400);
    // 按比例计算fontSize
    let fontSize = (clientWidth / designWidth) * baseSize;
    // 最大不超过16px
    fontSize = Math.min(fontSize, 16);
    rem.value = fontSize;
    // 只有canvas挂载后才重绘
    nextTick(() => {
        if (canvasRef.value) redraw();
    });
}
// 监听窗口变化
window.addEventListener("resize", setRemBase);
onMounted(() => {
    rem.value = 16;
    canvasWidth.value = 87.5;
    canvasHeight.value = 56.25;
    setRemBase();
    loadImages();
});
</script>

<style scoped>
.active {
    fill: #ffd700;
}
.canvas-stack {
    /* 让画布撑满父容器 */
    width: 100%;
    height: 100%;
    position: relative;
}
</style>